using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace Crainiate.Data.Persistence.Cache
{
    public abstract class ReflectionParserBase
    {
        //Property variables
        private string _name;
        private string _fullName;
        private bool _isUnderlying;
        private string _underlyingName;
        private Type _underlyingType;
        private Type _keyType;
        private Type _persistType;
        private Type _baseType;

        private bool _hasObjects;
        private bool _isIdentity;
        private bool _isView;
        private bool _hasPrimaryKey;
        private bool _hasCompoundKeys;
        private bool _hasForeignKeys;
        private bool _hasBinaryProperties;

        private int _rank;

        private PropertyInfo _primaryKey;
        private PropertyInfo _identity;
        private PropertyInfo[] _properties;
        private PropertyInfo[] _foreignKeys;
        private PropertyInfo[] _compoundKeys;
        private PropertyInfo[] _valueProperties;
        private PropertyInfo[] _referenceProperties;
        private PropertyInfo[] _binaryProperties;

        #region Interface

        //Properties
        public abstract Type[] Types {get; }

		public bool IsUnderlying
		{
			get
			{
				return _isUnderlying;
			}
            set
            {
                _isUnderlying = value;
            }
		}

        public bool IsIdentity
        {
            get
            {
                return _isIdentity;
            }
            set
            {
                _isIdentity = value;
            }
        }

        public bool IsView
        {
            get
            {
                return _isView;
            }
            set
            {
                _isView = value;
            }
        }

        public bool HasPrimaryKey
        {
            get
            {
                return _hasPrimaryKey;
            }
            set
            {
                _hasPrimaryKey = value;
            }
        }

        public bool HasCompoundKeys
        {
            get
            {
                return _hasCompoundKeys;
            }
            set
            {
                _hasCompoundKeys = value;
            }
        }

        public bool HasForeignKeys
        {
            get
            {
                return _hasForeignKeys;
            }
            set
            {
                _hasForeignKeys = value;
            }
        }

        public bool HasObjects
        {
            get
            {
                return _hasObjects;
            }
            set
            {
                _hasObjects = value;
            }
        }

        public bool HasBinaryProperties
        {
            get
            {
                return _hasBinaryProperties;
            }
            set
            {
                _hasBinaryProperties = value;
            }
        }

		public string Name
		{
			get
			{
				return _name;
			}
            set
            {
                _name = value;
            }
		}

		public string FullName
		{
			get
			{
				return _fullName;
			}
            set
            {
                _fullName = value;
            }
		}

		public string UnderlyingName
		{
			get
			{
				return _underlyingName;
			}
            set
            {
                _underlyingName = value;
            }
		}

		public Type UnderlyingType
		{
			get
			{
				return _underlyingType;
			}
            set
            {
                _underlyingType = value;
            }
		}

        public Type BaseType
        {
            get
            {
                return _baseType;
            }
            set
            {
                _baseType = value;
            }
        }

		public Type KeyType
		{
			get
			{
				return _keyType;
			}
            set
            {
                _keyType = value;
            }
		}

        public Type PersistType
        {
            get
            {
                return _persistType;
            }
            set
            {
                _persistType = value;
            }
        }

        public PropertyInfo PrimaryKey
        {
            get
            {
                return _primaryKey;
            }
            set
            {
                _primaryKey = value;
            }
        }

        public PropertyInfo Identity
        {
            get
            {
                return _identity;
            }
            set
            {
                _identity = value;
            }
        }

        public PropertyInfo[] Properties
        {
            get
            {
                return _properties;
            }
            set
            {
                _properties = value;
            }
        }

        public PropertyInfo[] ForeignKeys
        {
            get
            {
                return _foreignKeys;
            }
            set
            {
                _foreignKeys = value;
            }
        }

        public PropertyInfo[] CompoundKeys
        {
            get
            {
                return _compoundKeys;
            }
            set
            {
                _compoundKeys = value;
            }
        }

        public PropertyInfo[] ValueProperties
        {
            get
            {
                return _valueProperties;
            }
            set
            {
                _valueProperties = value;
            }
        }

        public PropertyInfo[] ReferenceProperties
        {
            get
            {
                return _referenceProperties;
            }
            set
            {
                _referenceProperties = value;
            }
        }

        public PropertyInfo[] BinaryProperties
        {
            get
            {
                return _binaryProperties;
            }
            set
            {
                _binaryProperties = value;
            }
        }

        public int Rank
        {
            get
            {
                return _rank;
            }
            set
            {
                _rank = value;
            }
        }

        //Methods
        public virtual void Reset()
        {
            _name = string.Empty;
            _fullName = string.Empty;
            _isUnderlying = false;
            _underlyingName = string.Empty;
            _underlyingType = null;
            _keyType = null;
            _persistType = null;

            _hasObjects = false;
            _isIdentity = false;
            _isView = false;
            _hasPrimaryKey = false;
            _hasCompoundKeys = false;
            _hasForeignKeys = false;
            _hasBinaryProperties = false;

            _primaryKey = null;
            _identity = null;
            _properties = null;
            _foreignKeys = null;
            _compoundKeys = null;
            _valueProperties = null;
            _referenceProperties = null;
            _binaryProperties = null;
        }

        public abstract void Parse(Type type);
        public abstract void Validate(Type type);

        protected static bool IsUnderlyingBase(Type type)
        {
            //Null
            if (type.BaseType == null) return true;

            //Object or other base class
            if (!typeof(IPersistable).IsAssignableFrom(type.BaseType)) return true;

            //View
            if (type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(View<>)) return true;

            return false;
        }

        //Return true if type is a base type or is an abstract base type
        protected static bool IsBaseAbstract(Type type)
        {
            if (IsUnderlyingBase(type)) return true;
            if (type.BaseType.IsAbstract) return IsBaseAbstract(type.BaseType);
            return false;
        }

        protected static Type GetBaseType(Type type)
        {
            if (IsUnderlyingBase(type)) return null;
            if (type.BaseType.IsAbstract) return GetBaseType(type.BaseType);
            return type.BaseType;
        }

        protected static int GetRank(Type type)
        {
            Type baseType = GetBaseType(type);
            
            if (baseType == null) return 0;
            
            return GetRank(baseType) + 1;          
        }

        #endregion
    }
}
